package org.springframework.dsw;

import org.springframework.dsw.annotation.Autowired;
import org.springframework.dsw.annotation.Component;
import org.springframework.dsw.annotation.ConfigurationProperties;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class BeanFactory {
    //存储反射的class文件名称
    private List<String> classNames = new ArrayList<>();
    //properties文件
    private final Properties contextConfig = new Properties();
    //默认的配置文件名称
    private final String contextConfigLocation = "applicaiton.properties";
    //一级缓存
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
    //三级缓存 存储ObjectFactory
    private final Map<String, ObjectFactory> singletonFactories = new HashMap(16);
    //二级缓存
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
    //扫描包配置属性名
    private final static String SCAN_PACKAGE = "scanPackage";
    //被扫描的包名
    private String scanPackage;
    //是否开启缓存
    private boolean isSingleton = true;
    //作用域
    private final static String SCORE = "score";
    //默认单例 可以通过配置文件改变
    private String score = "single";


    public BeanFactory(){
        //初始化Bean工厂
        initBeanFactory(contextConfigLocation);
    }

    public BeanFactory(String configPath){
        initBeanFactory(configPath);
    }



    private void initBeanFactory(String configPath){
        //加载配置文件
        this.loadConfig(configPath);
        //初始化配置
        this.initConfig();
        //扫描包
        this.doScannerPackage(scanPackage);
        //将包下需要注册的Bean注册进Bean工厂
        this.doInstance();
    }

    private void initConfig() {
        //获取包名
        this.scanPackage = contextConfig.getProperty(SCAN_PACKAGE);
        //获取作用域
        String score = contextConfig.getProperty(SCORE);
        //如果没有配置作用域则使用默认配置
        if(score != null && !score.equals("")){
            this.score = score;
        }
    }

    public Object getBean(String beanName) {
        Object bean = null;
        try {
            //如果开启缓存并且作用域是单例则从缓存内获取
            if (this.isSingleton && this.score.equals("single")) {
                bean = this.getSingleton(beanName);
                if (bean == null) {
                    this.createBean(beanName,Class.forName(scanPackage+"."+toCaseFirstCase(beanName)));
                    bean = this.getSingleton(beanName);
                }
                return bean;
            }
            //否则直接创建新的Bean
            bean = Class.forName(scanPackage+"."+toCaseFirstCase(beanName)).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }

    private Object getSingleton(String beanName) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            //双检锁操作
            if (singletonObject == null) {
                synchronized (this.singletonObjects) {
                    //从一级缓存中获取
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        //从二级缓存中获取
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) {
                            //如获取不到则获取ObjectFactory生成Bean
                            ObjectFactory singletonFactory = (ObjectFactory) this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
                                singletonObject = singletonFactory.getObject();
                                //存到二级缓存中移除出三级缓存
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }
        return singletonObject;
    }
    private void doInstance() {
        if (classNames == null) {
            return;
        }
        try {
            //遍历class列表
            for (String className : classNames) {
                //获取class对象
                Class<?> clazz = Class.forName(className);
                //只处理Component和ConfigurationProperties修饰的class
                if(clazz.isAnnotationPresent(Component.class)){
                    Component component = clazz.getAnnotation(Component.class);
                    //如果value没有填则按默认的类名小写开头作为beanName
                    String beanName = component.value();
                    if("".equals(beanName)){
                        beanName = toLowerFirstCase(clazz.getSimpleName());
                    }
                    this.createBean(beanName,clazz);
                }else if(clazz.isAnnotationPresent(ConfigurationProperties.class)){
                    ConfigurationProperties configurationProperties = clazz.getAnnotation(ConfigurationProperties.class);
                    String beanName = configurationProperties.value();
                    if("".equals(beanName)){
                        beanName = toLowerFirstCase(clazz.getSimpleName());
                    }
                    this.createBean(beanName,clazz);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private boolean isExistCache(String beanName){
        if(this.singletonObjects.containsKey(beanName)){
            return true;
        }
        if(this.singletonFactories.containsKey(beanName)){
            return true;
        }
        if(this.earlySingletonObjects.containsKey(beanName)){
            return true;
        }
        return false;
    }

    private void createBean(String beanName, Class<?> clazz) throws InstantiationException, IllegalAccessException {
        //如果缓存里面有这个Bean则直接返回
        if(this.isExistCache(beanName)){
            return;
        }
        //反射获取Bean
        Object bean = clazz.newInstance();
        if (this.isSingleton) {
            //开启缓存则先把Bean暴露到三级缓存 ObjectFactory
            this.addSingletonFactory(beanName, getObjectFactory(bean));
        }
        //属性注入
        this.populateBean(clazz,bean);
        //实例化Bean
        this.initializeBean(beanName,bean);
    }

    private void initializeBean(String beanName, Object bean) {
        //将Bean存入一级缓存
        this.addSingleton(beanName,bean);
    }

    private void populateBean(Class<?> clazz,Object bean) throws IllegalAccessException {
        //反射获取字段
        Field[] fields= clazz.getDeclaredFields();
        for (Field field : fields) {
            //强制访问private变量
            field.setAccessible(true);
            //获取字段名
            String fieldName = field.getName();
            if(clazz.isAnnotationPresent(ConfigurationProperties.class)){
                //当前类如果是ConfigurationProperties修饰的则从配置文件里面获取变量
                ConfigurationProperties configurationProperties = clazz.getAnnotation(ConfigurationProperties.class);
                String value = contextConfig.getProperty(configurationProperties.prefix()+"."+fieldName);
                field.set(bean,value);
                continue;
            }
            if(field.isAnnotationPresent(Autowired.class)){
                //获取带Autowired注解的变量
                Autowired autowired = field.getAnnotation(Autowired.class);
                String beanName = autowired.value().trim();
                //根据bean名称获取
                if("".equals(beanName)){
                    beanName = field.getName();
                }
                field.set(bean,this.getBean(beanName));
            }
        }
    }

    private ObjectFactory getObjectFactory(Object bean){
        SmiplObjectFactory objectFactory = new SmiplObjectFactory();
        objectFactory.setObject(bean);
        return objectFactory;
    }

    protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {
        synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                //存入三级缓存中并移除出二级缓存
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
            }
        }
    }

    private void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            //存入一级缓存 其他缓存容器中的bean移除
            this.singletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
        }
    }

    private void doScannerPackage(String scanPackage) {
        //根据报名获取file
        URL url = this.getClass().getResource("/"+scanPackage.replaceAll("\\.","/"));
        File classPath = new File(url.getFile());
        for (File file:classPath.listFiles()){
            if(!file.isDirectory()){
                //只处理class文件
                if(file.getName().endsWith(".class")) {
                    String className = (scanPackage + "." + file.getName().replace(".class", ""));
                    //将扫描到的class文件路径保存
                    classNames.add(className);
                }
                continue;
            }
            //如果是目录则递归目录下的class文件
            doScannerPackage(scanPackage+"."+file.getName().replace(".class",""));
        }
    }

    private void loadConfig(String configPath){
        //获取配置文件流
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(configPath);
        try {
            //加载配置文件
            contextConfig.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(is != null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private String toLowerFirstCase(String simpleName) {
        //转码 大写转小写
        char [] chars = simpleName.toCharArray();
        chars[0]+=32;
        return String.valueOf(chars);
    }


    private String toCaseFirstCase(String simpleName) {
        //小写转大写
        char [] chars = simpleName.toCharArray();
        chars[0]-=32;
        return String.valueOf(chars);
    }
}
